import java.util.*;
import java.io.*;

public class Exp {
    private static final int MIN_LENGTH = 1024 * 2014 * 10;
    private static final int BLOCK_SIZE = 8192;

    public static void main(String[] args) {
        System.out.println("此程序用于将一个大型文件拆分为若干小文件。");
        System.out.println("要拆分的文件的路径（支持绝对路径或相对路径）：");
        System.out.print(">");
        Scanner cin = new Scanner(System.in);
        String path = cin.nextLine();
        File srcFile = new File(path);
        if (!srcFile.exists() || !srcFile.isFile()) {
            System.out.println("文件不存在。请检查您的输入，然后重试。");
            return;
        }
        long length;
        try (RandomAccessFile file = new RandomAccessFile(srcFile, "r");) {
            length = file.length();
        }
        catch (IOException ex) {
            System.out.println("在试图读取文件时发生IO异常，程序被迫中止。");
            return;
        }
        if (length <= MIN_LENGTH) {
            System.out.println("文件小于10MB，仍要拆分吗？(Y/N)");
            System.out.print(">");
            String choice = cin.nextLine();
            if (!(choice.equals("Y") || choice.equals("y") || choice.equals("yes") || choice.equals("Yes") || choice.equals("YES"))) {
                return;
            }
        }
        System.out.println("您希望将它拆分为几段？");
        System.out.print(">");
        int numOfSegments = Integer.parseInt(cin.nextLine());
        if (numOfSegments <= 0) {
            System.out.println("无效的输入，请检查您的输入并重试。");
            return;
        }
        if (length / numOfSegments < MIN_LENGTH) {
            System.out.println("拆分后每段将小于10MB，仍要拆分吗？");
            System.out.print(">");
            String choice = cin.nextLine();
            if (!(choice.equals("Y") || choice.equals("y") || choice.equals("yes") || choice.equals("Yes") || choice.equals("YES"))) {
                return;
            }
        }
        long segmentLength = length / numOfSegments;
        segmentLength += 1; //避免生成numOfSegments+1段
        if (segmentLength < 1) segmentLength = 1;
        int numOfDotsPrinted = 0;
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(srcFile), BLOCK_SIZE)) {
            long sum = 0;
            int id = 1;
            //创建最后一个分段后，恰好读完源文件则终止
            while (sum < length) {
                createSegment(path, id, segmentLength, in);
                id++;
                sum += segmentLength;
                //更新进度条
                if ((1.0 * sum) / length > numOfDotsPrinted * 0.1) {
                    int numOfDotsToPrint = (int) ((1.0 * sum) / length * 10) - numOfDotsPrinted;
                    for (int i = 1; i <= numOfDotsToPrint; i++)
                        System.out.print(".");
                    numOfDotsPrinted += numOfDotsToPrint;
                }
            }
        }
        //创建分段中途遭遇文件结尾则终止
        catch (EOFException ex) {

        }
        catch (IOException ex) {
            System.out.println("在拆分文件的过程中发生IO异常，程序被迫中止。");
            return;
        }
        if (numOfDotsPrinted < 10) {
            for (int i = 1; i <= 10 - numOfDotsPrinted; i++) System.out.print(".");
        }
        System.out.println("完成");
    }

    private static void createSegment(String srcFilePath, int id, long segmentLength,
                                      BufferedInputStream in) throws IOException {
        int dotIndex = srcFilePath.lastIndexOf('.');
        if (dotIndex == -1) dotIndex = srcFilePath.length(); //无后缀文件
        StringBuilder segmentPath = new StringBuilder(srcFilePath);
        segmentPath.insert(dotIndex, " - " + id);
        byte[] arr = new byte[8192];
        try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(segmentPath.toString()), BLOCK_SIZE);) {
            int ret;
            long sum = 0; //已经读取的字节数
            int readLength; //每次需要读取的字节数（动态变化）
            while (true) {
                if (segmentLength - sum < BLOCK_SIZE) readLength = (int) (segmentLength - sum);
                else readLength = BLOCK_SIZE;
                //已经完成本块的读取
                if (readLength == 0) break;
                ret = in.read(arr, 0, readLength);
                //到达文件尾，且未读取任何数据
                if (ret == -1) throw new EOFException();
                sum += ret;
                out.write(arr, 0, ret);
                //到达文件尾，但已经读取一些数据
                if (ret < readLength) throw new EOFException();
            }
        }
    }
}